Implicit Teardown
The book has now been published and the content of this chapter has likely changed substanstially.Please see page 516 of xUnit Test Patterns for the latest information.
Also known as: Hooked Teardown, Framework-invoked Teardown, Teardown Method
How do we tear down the Test Fixture?
The Test Automation Framework calls our clean up logic in the tearDown method after every Test Method.
Sketch Implicit Teardown embedded from Implicit Teardown.gif
A large part of making tests repeatable and robust is ensuring that the test fixture is torn down after each test. Leftover objects and database records, open files and connections can at best cause performance degradations and at worst cause tests to fail or systems to crash.
When we can't take advantage of Garbage-Collected Teardown (page X) and we have several tests with the same objects to tear down, we can put the tear down clogic into a special tearDown method that the Test Automation Framework (page X) calls after each Test Method (page X) is run.
How It Works
Anything that needs to be cleaned up can be freed or destroyed in the final phase of the Four-Phase Test (page X), namely the fixture teardown phase. Most members of the XUnit family of Test Automation Frameworks support the concept of Implicit Teardown wherein they call the tearDown method of each Testcase Object (page X) after the Test Method has been run.
The tearDown method is called regardless of whether the test passes or fails. This ensures that we have the opportunity to clean up undisturbed by any failed assertions. Beware, however, that some members of the family do not call tearDown if the setUp method raises an error.
When To Use It
We can use Implicit Teardown whenever we have several tests with the same resources that need to be destroyed or freed explicitly after the test has been completed and those resources will not be destroyed or freed automatically. We may discover this because we have Unrepeatable Tests (see Erratic Test on page X) or Slow Tests (page X) caused by the accumulation of detritus from many test runs.
If the objects created by the test are internal resources and subject to automated memory management then Garbage-Collected Teardown may eliminate a lot of work for us. If each test has a completely different set of objects to teardown then Inline Teardown (page X) may be more appropriate. In many cases, manually written teardown logic can be avoided entirely with Automated Teardown (page X).
Implementation Notes
The teardown logic in the tearDown method is most commonly discovered by refactoring from tests that had Inline Teardown. The tearDown method may need to be "flexible" or "accomodating" for several reasons:
- When a test fails or when a test error occurs, the Test Method may not have created all the fixture objects.
- If all the Test Methods in the Testcase Class (page X) don't use identical fixtures (That is, they augment the Implicit Teardown with some additional Inline Setup (page X) or Delegated Setup (page X)) there may be different sets of objects to clean up for different tests.
Variation: Teardown Guard Clause
We can avoid arbitrarily Conditional Test Logic (page X) to deal with the case where only a subset of the objects to be torn down are actually present by putting a guard clause (a simple if statement) around each tear down operation to guard against the resource not being present. Given this technique, a suitably coded tearDown method can tear down various fixture configurations. Contrast this with the setUp method which can only set up the lowest common denominator fixture for the Test Methods that share it.
Motivating Example
The test below sets up several standard objects during fixture setup. Because the objects are persisted in a database, they must be cleaned up explicitly after every test. Each test (I have only shown one of severeal) contains the same inline teardown logic to delete the objects.
public void testGetFlightsByOrigin_NoInboundFlight_SMRTD() throws Exception { // Fixture setup BigDecimal outboundAirport = createTestAirport("1OF"); BigDecimal inboundAirport = null; FlightDto expFlightDto = null; try { inboundAirport = createTestAirport("1IF"); expFlightDto = createTestFlight(outboundAirport, inboundAirport); // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(inboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); } finally { try { facade.removeFlight(expFlightDto.getFlightNumber()); } finally { try { facade.removeAirport(inboundAirport); } finally { facade.removeAirport(outboundAirport); } } } } Example SafeMultiResourceGuaranteedTeardown embedded from java/com/clrstream/ex6/services/test/InlineTeardownExampleTest.java
There is enough Test Code Duplication (page X) here to warrant converting these tests to Implicit Teardown.
Refactoring Notes
First, we find the most representative example of teardown in all the tests. Then, we do an Extract Method[Fowler] refactoring on that code and call the resulting method tearDown. Finally, we delete the teardown logic in each of the other tests. We may need to introduce Teardown Guard Clauses around any tear down logic that may not be needed in every test. We should also surround each tear down attempt with a try/finally to ensure the remaining teardown logic executes even if an earlier one fails.
Example: Implicit Teardown
In this example, we see the same tests with the teardown logic moved to the tearDown method. We have also introduced a series of nested try/finally blocks around teardown operations to make sure that a error raised in one doesn't prevent the remaining ones from being executed. Note how much smaller the tests have become.
BigDecimal outboundAirport; BigDecimal inboundAirport; FlightDto expFlightDto; public void testGetFlightsByAirport_NoInboundFlights_NIT() throws Exception { // Fixture setup outboundAirport = createTestAirport("1OF"); inboundAirport = createTestAirport("1IF"); expFlightDto = createTestFlight( outboundAirport, inboundAirport); // Exercise System List flightsAtDestination1 = facade.getFlightsByOriginAirport(inboundAirport); // Verify Outcome assertEquals(0,flightsAtDestination1.size()); } Example TeardownFreeMultiResourceTest embedded from java/com/clrstream/ex6/services/test/ManualTeardownExampleTest.java
protected void tearDown() throws Exception { try { facade.removeFlight( expFlightDto.getFlightNumber() ); } finally { try { facade.removeAirport(inboundAirport); } finally { facade.removeAirport(outboundAirport); } } } Example SafeMultiResourceImplicitTeardown embedded from java/com/clrstream/ex6/services/test/SafeMultiResourceImplicitTeardown.java
Note that there is no try/finally block around the exercising of the system under test (SUT) and the assertions. This helps the test reader understand that this is not an Expected Exception Test (see Test Method). Also, note that we didn't need to put a Guard Clause[SBPP] in front of each operation as the try/finally ensures that a failure is non-catastrophic so there is no real harm in going ahead and trying.
Copyright © 2003-2008 Gerard Meszaros all rights reserved